﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace SoftKeyBoard
{
    /// <summary>
    /// 软件盘打开或关闭的帮助类，暴露了IsKeyboardOpen()、OpenKeyBoard()和CloseKeyboard()
    /// </summary>
    public class SoftKeyHelper
    {
        private const int WM_CLOSE = 0x0010;

        /// <summary>
        /// The window is disabled. See http://msdn.microsoft.com/en-gb/library/windows/desktop/ms632600(v=vs.85).aspx.
        /// </summary>
        public const UInt32 WS_DISABLED = 0x8000000;

        /// <summary>
        /// Specifies we wish to retrieve window styles.
        /// </summary>
        //public const int GWL_STYLE = -16;

        [DllImport("user32.dll")]
        public static extern IntPtr FindWindow(String sClassName, String sAppName);
        private const int GWL_EXSTYLE = -20;//获得扩展窗口风格。
        private const int GWL_HINSTANCE = -6;//获得应用实例的句柄。
        private const int GWL_HWNDPARENT = -8;//如果父窗口存在，获得父窗口句柄。
        private const int GWL_ID = -12;//获得窗口标识。
        private const int GWL_STYLE = -16;//获得窗口风格。
        private const int GWL_USERDATA = -21;//获得与窗口有关的32位值。每一个窗口均有一个由创建该窗口的应用程序使用的32位值。
        private const int GWL_WNDPROC = -4;//获得窗口过程的地址，或代表窗口过程的地址的句柄。必须使用CallWindowProc函数调用窗口过程。
         
        private const int WS_EX_TOPMOST = 0x00000008;
        [DllImport("user32.dll", SetLastError = true)]
        static extern long GetWindowLong(IntPtr hWnd, int nIndex);

        private const int SW_HIDE = 0;
        private const int SW_NORMAL = 1;
        private const int SW_MAXIMIZE = 3;
        private const int SW_SHOWNOACTIVATE = 4;
        private const int SW_SHOW = 5;
        private const int SW_MINIMIZE = 6;
        private const int SW_RESTORE = 9;
        private const int SW_SHOWDEFAULT = 10;

        [DllImport("user32.dll")]
        public static extern int ShowWindow(IntPtr hwnd,int nCmdShow);

        /// <summary>
        /// Gets the window handler for the virtual keyboard.
        /// </summary>
        /// <returns>The handle.</returns>
        public IntPtr GetKeyboardWindowHandle()
        {
            return FindWindow("IPTip_Main_Window", null);
        }

        [DllImport("kernel32.dll")]
        public static extern uint GetLastError();

        IntPtr _keyboardHandle = IntPtr.Zero;

        /// <summary>
        /// 判断窗口是否为TopMost
        /// </summary>
        /// <param name="hWnd"></param>
        /// <returns></returns>
        public long CheckIsTopMost()
        {
            long style = GetWindowLong(_keyboardHandle, GWL_STYLE);
            if (0 == style)
            {
                return GetLastError();
            }
            return style;          
        }
        /// <summary>
        /// 检测软键盘是否已经打开了
        /// </summary>
        /// <returns>如果打开，返回true，否则返回false</returns>
        public bool IsKeyboardOpen()
        {     
            _keyboardHandle = GetKeyboardWindowHandle();
            if (_keyboardHandle != IntPtr.Zero)
            {
                return true;
            }
            else
            {
                return false;
            }

            //以下是采用检查是否存在主窗口来判断是否打开
            //int id = _softKeyboardProc.Id;
            //IntPtr ptr = GetMainWindowHandle(_softKeyboardProc.Id);  //通过检查其主窗口是否还在。也可以用Process.HasExited属性
            //return ptr != IntPtr.Zero;
        }

        public void ShowKeyBoard()
        {
            if (_keyboardHandle != IntPtr.Zero)
            {
                ShowWindow(_keyboardHandle, SW_RESTORE);
            }
        }

        private string _keybaordFileName = "";
       
        /// <summary>
        /// 打开软键盘。调用之前应先通过IsKeyboardOpen()来检查keyboard是否已经被打开，如果已经打开，就不要再打开了
        /// </summary>

        public void OpenKeyBoard()
        {
            if (EnvironmentEx.GetOSVersion() == OSVersion.Win10)
            {
                _keybaordFileName = @"C:/Program Files/Common Files/microsoft shared/ink/TabTip.exe";//"";//Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.SystemX86) , );
            }
            else
            {
                _keybaordFileName = Path.Combine(Application.StartupPath, "osk.exe"); ;
            }
            if (!System.IO.File.Exists(_keybaordFileName))
            {
                MessageBox.Show("软键盘可执行文件osk.exe不存在！");
                return;
            }
            try
            {
                System.Diagnostics.Process softKeyboardProc;
                // Start a process and raise an event when done.
                softKeyboardProc = new System.Diagnostics.Process();
                softKeyboardProc.StartInfo.FileName = _keybaordFileName;
                softKeyboardProc.EnableRaisingEvents = true;  //允许退出
                softKeyboardProc.Exited += new EventHandler(SoftKeyboard_Exited);  //如果退出，则调用回调函数来关闭对象
                softKeyboardProc.Start();
            }
            catch (Exception ex)
            {
                Console.WriteLine("打开文件  \"{0}\" 时发生错误:" + "\n" + ex.Message, _keybaordFileName);
                return;
            }
        }

        /// <summary>
        /// 关闭软键盘
        /// </summary>
        public void CloseKeyBoard()
        {         
            Process[] processes;
            processes = Process.GetProcesses();
            foreach (Process p in processes)
            {
                try
                {
                    if (p.ProcessName.ToUpper() == "TABTIP" || p.ProcessName.ToUpper() == "OSK")
                    {
                        p.Kill();
                        p.WaitForExit();
                        p.Close();
                    }
                }
                catch
                {
                }
            }
        }



        /// <summary>
        /// 当键盘被手工关闭的时候，调用该函数，关闭_softKeyboardProc对象并将_keyBoardWindowHwnd置为IntPtr.Zero
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SoftKeyboard_Exited(object sender, EventArgs e)
        {
           
        }

        /// <summary>
        /// 根据进程ID，获取其主窗口
        /// </summary>
        /// <param name="processId"></param>
        /// <returns></returns>
        private static IntPtr GetMainWindowHandle(int processId)
        {
            IntPtr MainWindowHandle = IntPtr.Zero;
            NativeMethods.EnumWindows(new NativeMethods.EnumWindowsProc((hWnd, lParam) =>
            {
                IntPtr PID;
                NativeMethods.GetWindowThreadProcessId(hWnd, out PID);
                if (PID == lParam && NativeMethods.IsWindowVisible(hWnd) && NativeMethods.GetWindow(hWnd, NativeMethods.GW_OWNER) == IntPtr.Zero)
                {
                    MainWindowHandle = hWnd;
                    return false;
                }
                return true;
            }), new IntPtr(processId));
            return MainWindowHandle;
        }
    }

    public static class NativeMethods
    {
        [DllImport("User32.dll", EntryPoint = "FindWindow")]
        public extern static IntPtr FindWindow(string lpClassName, string lpWindowName);

        [System.Runtime.InteropServices.DllImportAttribute("user32.dll", EntryPoint = "MoveWindow")]
        public static extern bool MoveWindow(System.IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern int SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int y, int Width, int Height, int flags);

        [DllImport("user32.dll")]
        public static extern bool SetForegroundWindow(IntPtr hWnd);

        [DllImport("User32.dll", EntryPoint = "SendMessage")]
        public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
        public const uint GW_OWNER = 4;
        public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern int GetWindowThreadProcessId(IntPtr hWnd, out IntPtr lpdwProcessId);

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern bool IsWindowVisible(IntPtr hWnd);
    }

}
